#include <video-graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <rtc.h>

#include "boss.h"
#include "game.h"
#include "window.h"
#include "xpm.h"
#include "anim.h"
#include "global.h"
#include "input.h"
#include "sound.h"

/** Duração de cada frame da animação do Boss */
#define BOSS_ANIM_MSEC 500

/** Largura da janela do boss */
#define BOSS_WIN_WIDTH HRES

/** Dados usados pelo Boss */
struct BossData {
	int num; /**< Número do boss */
	Animation anim; /**< Animação do boss */
	Window win; /**< Janela do boss */
};

/** Escolhas*/
enum GameChoice {
	CHOICE_ROCK,
	CHOICE_PAPER,
	CHOICE_SCISSORS,
	NUM_CHOICES
};

/** Dados usados no jogo do boss */
struct GameData {
	// Dados usados no jogo do galo
	int num; /**< Número do boss */
	int choice; /**< O que o jogador escolheu */
	RTC_TIME init; /**< Quando o jogo começou */

	Animation alexanim; /**< Animação do Alex Kidd */
	Animation gameanim; /**< Animação do Alex Kidd */
	Animation bossanim; /**< Animação do Boss */

	int alex_wins;	/**< Número de vitórias do Alex */
	int boss_wins;  /**< Número de vitórias do Boss */
	XPM boss_choice; /**< Escolha do boss */
	Bool in_game; /**< Estamos jogando? */
};

/** Lê dados do teclado */
static Bool boss_keyboard_cb (unsigned char scan_code, void *data)
{
	struct  GameData *bdata = data;

	if (bdata->in_game) {
		if (scan_code == SC_UP || scan_code == SC_RIGHT) {
			bdata->choice--;
			if (bdata->choice < 0) {
				bdata->choice = 2;
			}
		} else if (scan_code == SC_DOWN || scan_code == SC_LEFT) {
			bdata->choice++;
			if (bdata->choice > 2) {
				bdata->choice = 0;
			}
		}
	} 
	// Não permite que outras funções leiam do teclado
	return true;
}

/** Desenha a janela do boss */
static void bossgame_show_cb (Window win, int x, int y, int w, int h, void *data)
{
	struct  GameData *bdata = data;
	XPM choices [NUM_CHOICES] = {xpm_get(XPM_ROCK), xpm_get(XPM_PAPER), xpm_get(XPM_SCISSORS)};
	
	// Fundo preto
	window_fill (win, 0);
	window_draw_string (win, "Mude a sua jogada com as setas do teclado", 10, 10, 15);
	// Hora
	RTC_TIME now;
	rtc_read_time (&now);

	RTC_TIME diff = subtract_time (&bdata->init, &now);
	char string [20];

	if (bdata->alex_wins < 3 && bdata->boss_wins < 3) {
		if (bdata->in_game)
			sprintf (string, "Tempo restante: %d", 5 - diff.sec);
		else
			sprintf (string, "Proxima partida: %d", 5 - diff.sec);
	}

	window_draw_string (win, string, 10, 30, 15);

	// Desenha as opções do jogo do galo
	xpm_draw (win,
		  choices[bdata->choice],
		  x + w/3 - choices[bdata->choice]->width/2, 
		  y + h/3 - choices[bdata->choice]->height/2);

	// Se estivermos jogando o boss pode trocar sua opção
	if (bdata->in_game)
		bdata->boss_choice = anim_continue (&bdata->gameanim);

		
	xpm_draw (win,
		  bdata->boss_choice,
		  x + 2*w/3 - bdata->boss_choice->width/2, 
		  y + h/3 - bdata->boss_choice->height/2);

	// Desenha os personagens
	XPM alex = anim_continue (&bdata->alexanim);
	xpm_draw (win,
		  alex,
		  x + w/3 - alex->width/2, 
		  y + h - 100 - alex->height);

	XPM boss = anim_continue (&bdata->bossanim);
	xpm_draw (win,
		  boss,
		  x + 2*w/3 - boss->width/2, 
		  y + h - 100 - boss->height);

	// Desenha  os pontos
	if (bdata->alex_wins < 3 && bdata->boss_wins < 3) {
		window_draw_int (win, bdata->alex_wins, x + w/3, y + h - 50, 15);
		window_draw_int (win, bdata->boss_wins, x + 2*w/3, y + h - 50, 15);
	} else if (bdata->alex_wins >= 3) {
		window_draw_string (win, ":D", x + w/3, y + h - 50, 15);
		window_draw_string (win, ":(", x + 2*w/3, y + h - 50, 15);
	} else {
		window_draw_string (win, ":(", x + w/3, y + h - 50, 15);
		window_draw_string (win, ":D", x + 2*w/3, y + h - 50, 15);
	}

}

/* Joga uma partida do jogo */
static void play_game (struct GameData *bdata, int tempo, Bool in_game)
{

	RTC_TIME diff; // Diferença
	
	bdata->in_game = in_game;
	rtc_read_time (&bdata->init);
	
	do {
		// Lê os dados do teclado
		input_keyboard_read ();
		// Mostra as janelas
		winsys_draw ();
		// Espera 20 milisegundos e toca música ao mesmo tempo
		sound_milisleep(20);
		// Lê do RTC
		RTC_TIME now;
		rtc_read_time (&now);

		diff = subtract_time (&bdata->init, &now);

	} while (diff.sec < tempo);

	// Sincroniza com o relógio
	if (!in_game)
		anim_play (&bdata->bossanim);
}

/** Joga contra o Boss
 *
 *  \param bossnum Número do boss
 * \return Verdadeiro se ganhamos o jogo
 */
static Bool boss_game (int num_boss)
{
	struct GameData data;

	// Inicializa
	data.num = num_boss;
	data.alex_wins = 0;
	data.boss_wins = 0;
	// Animações
	anim_init (&data.bossanim, ANIM_BOSS_1 + num_boss - 1, BOSS_ANIM_MSEC);
	anim_play (&data.bossanim);
	anim_init (&data.alexanim, ANIM_ALEX_WR, BOSS_ANIM_MSEC/2);
	anim_play (&data.alexanim);
	anim_init (&data.gameanim, ANIM_GAME, 1000/ (1 << (num_boss - 1)));
	data.boss_choice = anim_play (&data.gameanim);

	data.choice = 0;


	XPM choices [NUM_CHOICES] = {xpm_get(XPM_ROCK), xpm_get(XPM_PAPER), xpm_get(XPM_SCISSORS)};


	// Obtém as coordenadas do sistema de janelas
	int sx, sy;
	winsys_get_coord (&sx, &sy);

	// Cria a janela
	Window win = window_new (sx, sy, HRES, VRES, &data);
	window_show (win, bossgame_show_cb);
	window_set_plane (win, PLANE_EXTRA + 10);
	
	// Regista o controlador do teclado
	void *kbid = input_keyboard_register (boss_keyboard_cb, &data, true);

	do {
		play_game (&data, 5, false);
		play_game (&data, 5, true);

		// Testa quem ganhou
		enum GameChoice alexc = data.choice;
		enum GameChoice bossc = (data.boss_choice == choices[CHOICE_ROCK] ? CHOICE_ROCK 
						: (data.boss_choice == choices[CHOICE_PAPER] ? CHOICE_PAPER : CHOICE_SCISSORS));
		if (alexc != bossc) {
			// Se não houve empate
			if ((alexc < bossc && (alexc != CHOICE_ROCK || bossc != CHOICE_SCISSORS))
				|| (bossc == CHOICE_ROCK && alexc == CHOICE_SCISSORS)) {
				// Se o boss ganhou
				if (data.alex_wins == 0) {
					data.boss_wins++;
				} else {
					data.alex_wins--;
				}
			} else {
				// Se o Alex ganhou
				if (data.boss_wins == 0) {
					data.alex_wins++;
				} else {
					data.boss_wins--;
				}
			}
		}
	} while (data.alex_wins < 3 && data.boss_wins < 3);
	// Mostra a gloriosa mensagem de vitória
	play_game (&data, 5, false);

	// Desfaz as mudanças
	input_keyboard_unregister (kbid);
	window_delete (win);

	//
	return data.alex_wins >= 3;
}

// Minijogo
static void boss_killed_cb (Window win, void *data)
{
	struct BossData *bdata = data;

	// Se o alex perdeu o jogo
	if (!boss_game (bdata->num)) {
		game_life_lose (); // Perde uma vida
		game_reload (); // Volta ao início
	} else {
		// Senão o jogo continua
		game_next ();
	}
	// Destroi a janela
	window_delete (bdata->win);
}

/* Mostra o BOSS */
WINDOW_DRAW_CB_ANIM (boss_show_cb, &data->anim, 0, 0, struct BossData)


/* Cria o strite do boss */
void boss_sprite_init (int x, int y, LevelSprite sprite)
{
	int bossnum;

	// Obtém o número do boss
	if (game_level_num() <= 3) {
		bossnum = 1;
	} else if (game_level_num() <= 6) {
		bossnum = 2;
	} else {
		bossnum = 3;
	}

		
	// Aloca os dados do boss
	struct BossData *data = malloc (sizeof (struct BossData));

	data->num = bossnum;
	
	// Inicializa as animações
	anim_init (&data->anim, ANIM_BOSS_1 + bossnum - 1, BOSS_ANIM_MSEC);

	XPM xpm = anim_play (&data->anim); // xpm usado para obter o tamanho da janela

	// Obtém a posição da janela
	int wx = (x+1) * xpm_level_cell_width - xpm->width;
	int wy = (y+1) * xpm_level_cell_height - xpm->height;

	// Criamos a janela
	data->win = window_new (wx, wy, BOSS_WIN_WIDTH, xpm->height, data);
	// Pomos a janela no plano dos inimigos
	window_set_plane (data->win, PLANE_ENEMY);
	// Fazemos a janela liberar a memória que alocamos
	window_set_free (data->win, free);

	// Mostramos a janela
	window_show (data->win, boss_show_cb);

	// Passamos ao nível os dados sobre este sprite
	level_sprite_init (sprite,
			   data->win,
			   boss_killed_cb,  // Se colidir com o boss o minijogo ocorre
			   level_callback_delwin, // Deleta a janela ao descarregar
			   xpm_level_cell_width,
			   xpm_level_cell_height,
			   data);
}

